package com.atguigu.gmall.common.config.pool;

import com.atguigu.gmall.common.config.pool.properties.properties.AppThreadPoolProperties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author:ahang
 * @create:2022-10-10 20:09
 * @Description:
 */
@EnableConfigurationProperties(AppThreadPoolProperties.class)
//1.开启AppThreadPoolConfiguration属性配置绑定
//2.将AppThreadPoolConfiguration放在容器中
@Configuration
public class AppThreadPoolConfiguration {
    /**
     * int corePoolSize,       核心大小
     * int maximumPoolSize,    最大大小（额外开（kat时间不用就要销毁）=最大大小-核心大小 ）
     * long keepAliveTime,     空闲时间kat
     * TimeUnit unit,          时间单位
     * BlockingQueue<Runnable> workQueue,    排队队列
     * ThreadFactory threadFactory,          线程工厂
     * RejectedExecutionHandler handler      拒绝策略：
     * 如果线程数量已经达到最大，而且队列满了。新任务再进来，利用拒绝策略进行拒绝；
     * 线上提交的任务都是要被执行的，如果线程池没满，就用线程池启动一个异步线程执行（用户感觉快）。
     * 如果线程池满了，让提交任务的当前线程，自己同步运行这个任务即可（用户感觉慢一点）。
     *
     * 获取AppThreadPoolProperties中属性值的办法:
     * 1.@autowrite自动注入
     * 2.@Bean标注的方法,如果有任何参数,这些参数默认都去容器中获取组件
     * @return
     */
    @Primary
    @Bean
    public ThreadPoolExecutor threadPoolExecutor(AppThreadPoolProperties threadPoolProperties,
        @Value("${spring.application.name}") String applicationName){
        return new ThreadPoolExecutor(threadPoolProperties.getCorePoolSize(),
                threadPoolProperties.getMaximumPoolSize(),
                threadPoolProperties.getKeepAliveTime(),
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(threadPoolProperties.getWorkQueueSize()),
                new ThreadFactory() { //线程池中所有的线程都是它new的
                    int i = 0; //单线程,线程start才开始异步调度,此时还不是异步,不涉及多并发操作
                    @Override
                    public Thread newThread(Runnable r) {
                        //此处可以增加分布式全线程数量限制：redis.incr(thread-count) < 100000);

                        //需求:1、线程池想要对接监控平台。
                        //2、监控平台自己从redis中拉取各种指标，进行可视化展示
                        //3、各种指标放到redis中,比如线程数量,线程的状态,线程的存活,
                        //    也可以任务包任务感知哪些线程跑起来

                        //任务包任务,利用层层封装完成所有任务
                        Thread thread = new Thread(()->{ //由cpu调度
                            //redis.incr("running...") 任务前置逻辑增强
                            r.run(); //跑真实任务,由线程池自己调度
                            //redis.decr("running...") 任务后置逻辑增强
                        });

                        thread.setName("【"+applicationName+"】-apppool-"+ (i++));
                        return thread;
                    }
                },
                new ThreadPoolExecutor.CallerRunsPolicy());
    }
}
